Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: Aliasing the v1 channels to v2 counterparty so that v1 channels can support v2 packets #7174

Closed
wants to merge 10 commits into from

Conversation

AdityaSripal
Copy link
Member

@AdityaSripal AdityaSripal commented Aug 14, 2024

Description

closes: #6975

The following is the simplest possible aliasing that preserves two different code paths for v1 and v2 packets.

The channel is converted into a v2 counterparty for the purpose of handling v2 packets. In this way, you can consider the connection and channel handshake as being very complicated ways to map the identifier (portID, channelID) to the client, and (cpPortId, cpChannelId) to the client counterparty.

This will allow v1 channels to immediately start using v2 packets while retaining the identifiers that they already use. This is critical for immediately enabling atomtic multi-packet data on existing transfer channels. As well as allowing existing channels to very easily upgrade their versions by simply sending a v2 packet with a new version rather than going through channel upgradability

  • Wire the ability to alias channels by converting Channel v1 into a channel v2 in a one-time fashion (write into state)
  • Test the aliasing ability specifically against transfer because this is the only important app to alias. Ensure denoms don't change when moving from v1 packet to v2 packet with same channel id
  • Decide on what to do with in-flight channel upgrades (ensure both sides have eureka enabled before disabling channel upgrades)
  • Rename Counterparty struct to Channel

Before we can merge this PR, please make sure that all the following items have been
checked off. If any of the checklist items are not applicable, please leave them but
write a little note why.

  • Targeted PR against the correct branch (see CONTRIBUTING.md).
  • Linked to GitHub issue with discussion and accepted design, OR link to spec that describes this work.
  • Code follows the module structure standards and Go style guide.
  • Wrote unit and integration tests.
  • Updated relevant documentation (docs/).
  • Added relevant godoc comments.
  • Provide a conventional commit message to follow the repository standards.
  • Include a descriptive changelog entry when appropriate. This may be left to the discretion of the PR reviewers. (e.g. chores should be omitted from changelog)
  • Re-reviewed Files changed in the GitHub PR explorer.
  • Review SonarCloud Report in the comment section below once CI passes.

@AdityaSripal AdityaSripal changed the base branch from main to feat/ibc-eureka August 14, 2024 14:32
@@ -95,6 +96,30 @@ func (k *Keeper) SetChannel(ctx sdk.Context, portID, channelID string, channel t
store.Set(host.ChannelKey(portID, channelID), bz)
}

// GetV2Counterparty returns a version 2 counterparty for the given port and channel ID
// by converting the channel into a version 2 counterparty
func (k *Keeper) GetV2Counterparty(ctx sdk.Context, portID, channelID string) (clienttypes.Counterparty, bool) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This effectively resolves the portID and channel ID into a counterparty. So the portID and channelID are in effect the clientID here. It's just a different arbitrary string identifier.

The one annoying thing is that the portID here is both acting as part of the v2 "client-id" (though we could move away from this with a different storage format since channel ids are unique) and also selecting the application to use.

This is fine for packets that are only sending one app data.

For MULTI_PACKET_DATA, we can deal with this by leaving the top portID as is, with the plan to eventually deprecate it. And then still allowing different ports in the internal packet data list

Copy link
Contributor

@crodriguezvega crodriguezvega left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, @AdityaSripal.

Taking transfer packets as an example, with the channel aliasing it will be possible to send a packet from an ibc-go v10 chain to another ibc-go v10 chain either on a existing transfer channel that was already set up or over eureka, right? Are we going to leave the decision of over which channel to send the packet to the user? If that's the case, would we need to add a protocol version field to MsgSendPacket? And the transfer application, which also sends a MsgSendPacket, should specify decide if the packet should be sent over a classic or an eureka channel, right?

It would be nice if there was a way by which we could internally determine if a counterparty chain supports eureka and then we prioritise sending the packet over a eureka channel instead of the existing classic channel. But I don't think that's going to be easy, right?

@@ -125,7 +125,7 @@ func (k Keeper) RecvPacket(
// Lookup counterparty associated with our channel and ensure that it was packet was indeed
// sent by our counterparty.
// Note: This can be implemented by the current keeper
counterparty, ok := k.ClientKeeper.GetCounterparty(ctx, packet.DestinationChannel)
counterparty, ok := k.GetCounterparty(ctx, packet.DestinationPort, packet.DestinationChannel)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have a transfer V2 packet sent from an ibc-go v10 chain to another ibc-go v10 chain over an existing channel, the packet's DestinationChannel will be filled in with the client ID of the destination chain, right? But then in the OnRecvPacket callback of the transfer the hash of the denomination will not match that of the tokens received previously (since those used the destination channel ID and not a client ID). Then those vouchers would not be fungible, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the DestinationChannel will continue to be the existing channel identifier (that's the purpose of this adjustment, to keep fungibility but allow existing channels to send v2 packets)

AdityaSripal and others added 5 commits August 30, 2024 17:34
# Conflicts:
#	modules/core/02-client/keeper/keeper_test.go
#	modules/core/02-client/types/client.go
#	modules/core/02-client/types/client.pb.go
#	modules/core/02-client/types/client_test.go
#	modules/core/02-client/types/errors.go
#	modules/core/02-client/types/msgs.go
#	modules/core/keeper/msg_server_test.go
#	modules/core/packet-server/keeper/keeper.go
#	modules/core/packet-server/keeper/keeper_test.go
#	modules/core/packet-server/types/keys.go
#	proto/ibc/core/client/v1/client.proto
#	testing/endpoint.go
Copy link
Contributor

@crodriguezvega crodriguezvega left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of changes had been done on the PR branch before it was synced with the feature branch, so I took me a bit of time to fix the conflicts and sync. I had to comment out some things (for which we can open an issue to keep track). I hope I didn't miss anything while fixing the conflicts. 🤞

Comment on lines +10 to +12
// Counterparty defines the counterparty for a light client to implement IBC eureka protocol
// this value is stored under our side's channel ID. which we will use to write all packet messages
// sent to counterparty
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Counterparty defines the counterparty for a light client to implement IBC eureka protocol
// this value is stored under our side's channel ID. which we will use to write all packet messages
// sent to counterparty
// Counterparty defines the counterparty for a light client to implement IBC eureka protocol
// this value is stored under our side's channel ID, which we will use to write all packet messages
// sent to counterparty.

Comment on lines +14 to +15
// the client identifier of the counterparty chain
// client id of the counterparty stored on our chain
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// the client identifier of the counterparty chain
// client id of the counterparty stored on our chain
// the client identifier of the light client representing the counterparty chain

// Counterparty defines the counterparty for a light client to implement IBC eureka protocol
// this value is stored under our side's channel ID. which we will use to write all packet messages
// sent to counterparty
message Counterparty {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think it'd be nice to see this Counterparty type as something used by historical situations (channel/port), but not necessarily only for that

option (gogoproto.goproto_getters) = false;

// unique identifier we will use to write all packet messages sent to counterparty
string channel_id = 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
string channel_id = 1;
string id = 1;

similar naming suggestion

if !ok {
return packetserver.Counterparty{}, false
}
merklePathPrefix := commitmentv2types.NewMerklePath(connection.Counterparty.Prefix.KeyPrefix, []byte(""))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

byte slice needs channel id info?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

think this comment is stale from when Aditya wanted to mush these together?

Comment on lines 36 to 39
counterparty, ok := k.GetCounterparty(ctx, sourceChannel)
if !ok {
return 0, errorsmod.Wrap(types.ErrCounterpartyNotFound, sourceChannel)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my understanding is that we basically want logic:

if channeltypes.IsChannelIDFormat(sourceChannel) {
    counterparty, ok = k.channelKeeper.GetV2Counterparty(ctx, sourcePort, sourceChannel)
} else {
    counterparty, ok = k.GetCounterparty(ctx, sourceChannel)
}
if !ok {
    return 0, errorsmod.Wrap(types.ErrCounterpartyNotFound, sourceChannel)
}

@colin-axner
Copy link
Contributor

colin-axner commented Sep 10, 2024

I think we should consider breaking up this pr for digestibility, I think there are some changes that should have been left for a followup.
1. Moving Counterparty type to packet server
2. Moving packet handlers out of keeper.go
3. Changing Counterparty structure
4. Implementing GetV2Counterparty usage
5. Ensuring no v1 packet code path disruption (channel upgrades)

message Counterparty {
// the client identifier of the counterparty chain
// client id of the counterparty stored on our chain
string client_id = 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be removed now. I see this mapping as:

key: my identifier (v1: channel/port, v2: clientid)
value: counterparty id, merkle path

The client id used to verify the counterparty is in the key, or can be resolved in the v1 case. It seems redundant atm, I would guess in the actual implementation it would only be referenced by the grpc, but we should probably create a new struct for grpc, like we did with the other types IdentifiedClientState for example

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

really we have a mapping from: our reserved key path -> counterparty reserved key path

resolving the client id responsible for the key path, should happen outside of this mapping?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before I forget, I went down the thought rabbit hole on this. The proposed construction works perfectly fine, but there's just some minor considerations one could have:

The current construction embeds the clientID into the Counterparty. When we handle v1 cases, the clientID is resolved and set into the counterparty type. In the v2 case, the value is redundant as it is in the key setting the value. The main benefit is code structure as it bundles all the necessary components into a single type. One could also return the clientID in the get counterparty function to achieve the same functionality.

The main reason to keep this approach, would be for future scenarios where we delete the channel/connection mappings and you want to migrate such that all v1 mappings are directly embedded into the counterparty type. One could have two mappings, as I hint at in the above comment. One to obtain the counterparties keyspace which is set for all v1/v2 and one to obtain the light client either directly in v2 case, or via a lookup in v1 case.

The reason why I went this rabbit hole was primarily due to naming. counterparty.clientID referring to your local client identifier, and not a client identifier on the counterparty, could be confusing. Alternatively, if you renamed the Counterparty to something like Connection or Channel, then connection.clientId and connection.counterpartyID could make sense. Either way, just a minor consideration.

@AdityaSripal
Copy link
Member Author

Closed and superceded by the feature branch with the merge of #7358

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

4 participants